package com.ltx.bugseeker.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ltx.bugseeker.common.ResultCode;
import com.ltx.bugseeker.entity.User;
import com.ltx.bugseeker.dto.SmsLoginDTO;
import com.ltx.bugseeker.dto.SetPasswordDTO;
import com.ltx.bugseeker.entry.dto.LoginDTO;
import com.ltx.bugseeker.entry.vo.LoginVO;
import com.ltx.bugseeker.entry.vo.UserVO;
import com.ltx.bugseeker.vo.SmsLoginVO;
import com.ltx.bugseeker.service.ISmsService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import com.ltx.bugseeker.exception.BusinessException;
import com.ltx.bugseeker.mapper.UserMapper;
import com.ltx.bugseeker.service.IUserService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

/**
 * 用户服务实现类
 * 实现用户相关的业务逻辑，包括登录、用户管理等功能
 *
 * @author ltx
 * @since 2025-10-20 14:43:42
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    
    @Resource
    private ISmsService smsService;

    /**
     * 最大登录失败次数
     */
    private static final int MAX_LOGIN_FAIL_COUNT = 5;

    /**
     * 账户锁定时长（分钟）
     */
    private static final int LOCK_MINUTES = 30;

    /**
     * 用户角色映射
     */
    private static final String[] ROLE_NAMES = {"", "管理员", "测试经理", "测试工程师", "开发工程师", "项目经理"};

    /**
     * 用户状态映射
     */
    private static final String[] STATUS_NAMES = {"", "正常", "禁用", "锁定"};

    /**
     * 用户登录
     * 验证用户凭据并生成访问令牌
     *
     * @param loginDTO 登录请求参数
     * @param request  HTTP请求对象，用于获取客户端信息
     * @return 登录响应信息，包含用户信息和令牌
     * @throws BusinessException 当登录失败时抛出业务异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoginVO login(LoginDTO loginDTO, HttpServletRequest request) {
        try {
            log.info("用户登录请求: username={}", loginDTO.getUsername());

            // 1. 参数校验
            validateLoginParams(loginDTO);

            // 2. 查询用户信息
            User user = getUserByUsername(loginDTO.getUsername());
            if (user == null) {
                log.warn("用户不存在: username={}", loginDTO.getUsername());
                throw new BusinessException(ResultCode.USER_NOT_FOUND);
            }

            // 3. 检查用户状态
            checkUserStatus(user);

            // 4. 验证密码
            if (!verifyPassword(loginDTO.getPassword(), user.getPassword())) {
                log.warn("密码错误: userId={}", user.getId());
                // 增加登录失败次数
                increaseLoginFailCount(user.getId());
                // 检查是否需要锁定账户
                if (user.getLoginFailCount() != null && user.getLoginFailCount() >= MAX_LOGIN_FAIL_COUNT - 1) {
                    lockUser(user.getId(), LOCK_MINUTES);
                    throw new BusinessException(ResultCode.ACCOUNT_LOCKED, "密码错误次数过多，账户已被锁定" + LOCK_MINUTES + "分钟");
                }
                throw new BusinessException(ResultCode.INVALID_PASSWORD);
            }

            // 5. 登录成功，重置失败次数
            resetLoginFailCount(user.getId());

            // 6. 更新最后登录信息
            updateLastLoginInfo(user.getId(), getClientIp(request));

            // 7. 生成令牌
            StpUtil.login(user.getId());
            String accessToken = StpUtil.getTokenValue();

            // 8. 构建登录响应
            LoginVO loginVO = LoginVO.builder()
                    .userId(user.getId())
                    .username(user.getUsername())
                    .realName(user.getRealName())
                    .email(user.getEmail())
                    .avatar(user.getAvatar())
                    .role(user.getRole())
                    .roleName(getRoleName(user.getRole()))
                    .lastLoginTime(user.getLastLoginTime())
                    .permissions(getUserPermissions(user.getRole()))
                    .build();

            log.info("用户登录成功: userId={}, username={}", user.getId(), user.getUsername());
            return loginVO;

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("用户登录异常: username={}", loginDTO.getUsername(), e);
            throw new BusinessException(ResultCode.INTERNAL_SERVER_ERROR, "登录失败");
        }
    }

    /**
     * 用户登出
     * 清除用户的登录状态和令牌
     *
     * @return true表示登出成功，false表示登出失败
     */
    @Override
    public boolean logout() {
        try {
            Long userId = null;
            if (StpUtil.isLogin()) {
                userId = StpUtil.getLoginIdAsLong();
            }
            log.info("用户登出: userId={}", userId);
            StpUtil.logout();
            return true;
        } catch (Exception e) {
            log.error("用户登出异常", e);
            return false;
        }
    }

    /**
     * 根据用户名查询用户
     * 支持用户名和邮箱两种方式查询
     *
     * @param username 用户名或邮箱
     * @return 用户实体对象，如果不存在则返回null
     */
    @Override
    public User getUserByUsername(String username) {
        if (StrUtil.isBlank(username)) {
            return null;
        }

        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.and(wrapper -> wrapper
                .eq(User::getUsername, username)
                .or()
                .eq(User::getEmail, username)
        );
        queryWrapper.eq(User::getDeleted, 0);

        return this.getOne(queryWrapper);
    }

    /**
     * 根据用户ID获取用户信息
     * 返回用户的详细信息，不包含敏感数据
     *
     * @param userId 用户ID
     * @return 用户视图对象
     * @throws BusinessException 当用户不存在时抛出业务异常
     */
    @Override
    public UserVO getUserInfo(Long userId) {
        User user = this.getById(userId);
        if (user == null) {
            throw new BusinessException(ResultCode.USER_NOT_FOUND);
        }

        return UserVO.builder()
                .id(user.getId())
                .username(user.getUsername())
                .email(user.getEmail())
                .phone(user.getPhone())
                .realName(user.getRealName())
                .avatar(user.getAvatar())
                .role(user.getRole())
                .roleName(getRoleName(user.getRole()))
                .status(user.getStatus())
                .statusName(getStatusName(user.getStatus()))
                .lastLoginTime(user.getLastLoginTime())
                .createTime(user.getCreateTime())
                .remark(user.getRemark())
                .build();
    }

    /**
     * 验证密码
     * 校验明文密码与加密密码是否匹配
     *
     * @param rawPassword     明文密码
     * @param encodedPassword 加密密码
     * @return true表示密码正确，false表示密码错误
     */
    @Override
    public boolean verifyPassword(String rawPassword, String encodedPassword) {
        if (StrUtil.isBlank(rawPassword) || StrUtil.isBlank(encodedPassword)) {
            return false;
        }
        return BCrypt.checkpw(rawPassword, encodedPassword);
    }

    /**
     * 更新用户最后登录信息
     * 记录用户的最后登录时间和IP地址
     *
     * @param userId  用户ID
     * @param loginIp 登录IP地址
     * @return true表示更新成功，false表示更新失败
     */
    @Override
    public boolean updateLastLoginInfo(Long userId, String loginIp) {
        try {
            LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(User::getId, userId)
                    .set(User::getLastLoginTime, LocalDateTime.now())
                    .set(User::getLastLoginIp, loginIp);

            return this.update(updateWrapper);
        } catch (Exception e) {
            log.error("更新用户最后登录信息异常: userId={}, loginIp={}", userId, loginIp, e);
            return false;
        }
    }

    /**
     * 增加登录失败次数
     * 用于账户安全策略，记录登录失败次数
     *
     * @param userId 用户ID
     * @return true表示更新成功，false表示更新失败
     */
    @Override
    public boolean increaseLoginFailCount(Long userId) {
        try {
            User user = this.getById(userId);
            if (user == null) {
                return false;
            }

            int failCount = user.getLoginFailCount() == null ? 0 : user.getLoginFailCount();

            LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(User::getId, userId)
                    .set(User::getLoginFailCount, failCount + 1);

            return this.update(updateWrapper);
        } catch (Exception e) {
            log.error("增加登录失败次数异常: userId={}", userId, e);
            return false;
        }
    }

    /**
     * 重置登录失败次数
     * 登录成功后重置失败计数
     *
     * @param userId 用户ID
     * @return true表示重置成功，false表示重置失败
     */
    @Override
    public boolean resetLoginFailCount(Long userId) {
        try {
            LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(User::getId, userId)
                    .set(User::getLoginFailCount, 0);

            return this.update(updateWrapper);
        } catch (Exception e) {
            log.error("重置登录失败次数异常: userId={}", userId, e);
            return false;
        }
    }

    /**
     * 锁定用户账户
     * 当登录失败次数过多时锁定账户
     *
     * @param userId      用户ID
     * @param lockMinutes 锁定时长（分钟）
     * @return true表示锁定成功，false表示锁定失败
     */
    @Override
    public boolean lockUser(Long userId, int lockMinutes) {
        try {
            LocalDateTime lockTime = LocalDateTime.now().plusMinutes(lockMinutes);

            LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(User::getId, userId)
                    .set(User::getStatus, 3) // 3-锁定
                    .set(User::getLockTime, lockTime);

            return this.update(updateWrapper);
        } catch (Exception e) {
            log.error("锁定用户账户异常: userId={}, lockMinutes={}", userId, lockMinutes, e);
            return false;
        }
    }

    /**
     * 检查用户账户状态
     * 验证用户是否可以正常登录
     *
     * @param user 用户实体对象
     * @throws BusinessException 当账户状态异常时抛出业务异常
     */
    @Override
    public void checkUserStatus(User user) {
        // 检查账户是否被禁用
        if (user.getStatus() == 2) {
            throw new BusinessException(ResultCode.ACCOUNT_DISABLED);
        }

        // 检查账户是否被锁定
        if (user.getStatus() == 3) {
            // 检查锁定时间是否已过期
            if (user.getLockTime() != null && LocalDateTime.now().isBefore(user.getLockTime())) {
                throw new BusinessException(ResultCode.ACCOUNT_LOCKED);
            } else {
                // 锁定时间已过期，解锁账户
                LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.eq(User::getId, user.getId())
                        .set(User::getStatus, 1) // 1-正常
                        .set(User::getLockTime, null)
                        .set(User::getLoginFailCount, 0);
                this.update(updateWrapper);
            }
        }
    }

    /**
     * 获取当前登录用户信息
     *
     * @param userId 用户ID
     * @return 登录用户信息VO对象
     * @throws BusinessException 当用户不存在时抛出业务异常
     */
    @Override
    public LoginVO getCurrentUserInfo(Long userId) {
        log.info("获取当前登录用户信息: userId={}", userId);

        try {
            // 根据用户ID查询用户信息
            User user = this.getById(userId);
            if (user == null) {
                throw new BusinessException(ResultCode.USER_NOT_FOUND, "用户不存在");
            }

            // 检查用户状态
            checkUserStatus(user);

            // 构建登录VO对象
            LoginVO loginVO = new LoginVO();
            loginVO.setUserId(user.getId());
            loginVO.setUsername(user.getUsername());
            loginVO.setRealName(user.getRealName());
            loginVO.setEmail(user.getEmail());
            loginVO.setAvatar(user.getAvatar());
            loginVO.setRole(user.getRole());
            loginVO.setRoleName(getRoleName(user.getRole()));
            loginVO.setLastLoginTime(user.getLastLoginTime());

            // 获取当前令牌信息
            String accessToken = StpUtil.getTokenValue();

            // 获取用户权限列表
            loginVO.setPermissions(getUserPermissions(user.getRole()));

            log.info("获取当前登录用户信息成功: userId={}, username={}", userId, user.getUsername());
            return loginVO;

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("获取当前登录用户信息异常: userId={}", userId, e);
            throw new BusinessException(ResultCode.INTERNAL_SERVER_ERROR, "获取用户信息失败");
        }
    }

    /**
     * 刷新访问令牌
     *
     * @param refreshToken 刷新令牌
     * @return 新的登录信息VO对象
     * @throws BusinessException 当刷新令牌无效时抛出业务异常
     */
    @Override
    public LoginVO refreshToken(String refreshToken) {
        log.info("刷新访问令牌: refreshToken={}", refreshToken);

        try {
            // 验证刷新令牌是否有效
            Object loginId = StpUtil.getLoginIdByToken(refreshToken);
            if (loginId != null) {
                // 令牌有效，获取用户ID
                Long userId = Long.valueOf(loginId.toString());
                
                // 检查用户是否存在
                User user = this.getById(userId);
                if (user == null) {
                    throw new BusinessException(ResultCode.USER_NOT_FOUND, "用户不存在");
                }
                
                // 检查用户状态
                checkUserStatus(user);
                
                // 重新登录以续期token
                StpUtil.login(userId);
                
                // 获取新的访问令牌
                String newAccessToken = StpUtil.getTokenValue();
                
                // 构建登录VO对象
                LoginVO loginVO = new LoginVO();
                loginVO.setUserId(user.getId());
                loginVO.setUsername(user.getUsername());
                loginVO.setRealName(user.getRealName());
                loginVO.setEmail(user.getEmail());
                loginVO.setAvatar(user.getAvatar());
                loginVO.setRole(user.getRole());
                loginVO.setRoleName(getRoleName(user.getRole()));
                loginVO.setLastLoginTime(user.getLastLoginTime());
                loginVO.setPermissions(getUserPermissions(user.getRole()));
                
                log.info("访问令牌刷新成功: userId={}, newAccessToken={}", userId, newAccessToken);
                return loginVO;
            } else {
                throw new BusinessException(ResultCode.INVALID_TOKEN, "刷新令牌已过期或无效");
            }

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("刷新访问令牌异常: refreshToken={}", refreshToken, e);
            throw new BusinessException(ResultCode.INVALID_TOKEN, "刷新令牌无效");
        }
    }

    /**
     * 检查用户名是否可用
     *
     * @param username 用户名
     * @return true表示可用，false表示已被使用
     * @throws BusinessException 当参数无效时抛出业务异常
     */
    @Override
    public boolean checkUsernameAvailability(String username) {
        log.info("检查用户名可用性: username={}", username);

        try {
            // 参数校验
            validateUsernameParam(username);

            // 查询用户名是否已存在
            User existUser = this.lambdaQuery()
                    .eq(User::getUsername, username.trim())
                    .eq(User::getDeleted, 0)
                    .one();

            boolean available = existUser == null;
            log.info("用户名可用性检查结果: username={}, available={}", username, available);

            return available;

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("检查用户名可用性异常: username={}", username, e);
            throw new BusinessException(ResultCode.SYSTEM_ERROR, "检查用户名失败");
        }
    }

    /**
     * 获取角色名称
     *
     * @param role 角色代码
     * @return 角色名称
     */
    private String getRoleName(Integer role) {
        if (role == null || role < 1 || role >= ROLE_NAMES.length) {
            return "未知角色";
        }
        return ROLE_NAMES[role];
    }

    /**
     * 获取状态名称
     *
     * @param status 状态代码
     * @return 状态名称
     */
    private String getStatusName(Integer status) {
        if (status == null || status < 1 || status >= STATUS_NAMES.length) {
            return "未知状态";
        }
        return STATUS_NAMES[status];
    }

    /**
     * 获取用户权限列表
     * 根据用户角色返回对应的权限标识
     *
     * @param role 用户角色
     * @return 权限列表
     */
    private List<String> getUserPermissions(Integer role) {
        // 这里可以根据实际业务需求配置不同角色的权限
        switch (role) {
            case 1: // 管理员
                return Arrays.asList("user:manage", "project:manage", "test:manage", "report:manage", "system:manage");
            case 2: // 测试经理
                return Arrays.asList("project:manage", "test:manage", "report:manage");
            case 3: // 测试工程师
                return Arrays.asList("test:execute", "test:manage", "report:view");
            case 4: // 开发工程师
                return Arrays.asList("test:view", "report:view");
            case 5: // 项目经理
                return Arrays.asList("project:view", "test:view", "report:view");
            default:
                return Arrays.asList("test:view");
        }
    }

    /**
     * 获取客户端IP地址
     *
     * @param request HTTP请求对象
     * @return 客户端IP地址
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        // 处理多个IP的情况，取第一个IP
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }

        return ip;
    }

    /**
     * 校验登录参数
     *
     * @param loginDTO 登录请求参数
     * @throws BusinessException 当参数校验失败时抛出业务异常
     */
    private void validateLoginParams(LoginDTO loginDTO) {
        // 用户名校验
        if (loginDTO.getUsername() == null || loginDTO.getUsername().trim().isEmpty()) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "用户名不能为空");
        }

        // 密码校验
        if (loginDTO.getPassword() == null || loginDTO.getPassword().trim().isEmpty()) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "密码不能为空");
        }

        // 用户名格式校验
        String username = loginDTO.getUsername().trim();
        if (!isValidUsername(username) && !isValidEmail(username) && !isValidMobile(username)) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "用户名格式不正确");
        }

        // 密码长度校验
        if (loginDTO.getPassword().length() < 6 || loginDTO.getPassword().length() > 20) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "密码长度应在6-20位之间");
        }

        // 验证码校验（如果需要）
        if (loginDTO.getCaptcha() != null && !loginDTO.getCaptcha().trim().isEmpty()) {
            if (loginDTO.getCaptchaKey() == null || loginDTO.getCaptchaKey().trim().isEmpty()) {
                throw new BusinessException(ResultCode.PARAM_ERROR, "验证码密钥不能为空");
            }
        }
    }

    /**
     * 校验用户名参数
     *
     * @param username 用户名
     * @throws BusinessException 当参数校验失败时抛出业务异常
     */
    private void validateUsernameParam(String username) {
        // 参数校验
        if (username == null || username.trim().isEmpty()) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "用户名不能为空");
        }

        String trimmedUsername = username.trim();

        // 格式校验
        if (!isValidUsername(trimmedUsername)) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "用户名格式不正确");
        }
    }

    /**
     * 验证用户名格式
     *
     * @param username 用户名
     * @return true表示格式正确，false表示格式错误
     */
    private boolean isValidUsername(String username) {
        if (username == null || username.trim().isEmpty()) {
            return false;
        }
        // 用户名只能包含字母、数字、下划线，长度3-20位
        return username.matches("^[a-zA-Z0-9_]{3,20}$");
    }

    /**
     * 验证邮箱格式
     *
     * @param email 邮箱
     * @return true表示格式正确，false表示格式错误
     */
    private boolean isValidEmail(String email) {
        if (email == null || email.trim().isEmpty()) {
            return false;
        }
        // 简单的邮箱格式验证
        return email.matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
    }

    /**
     * 验证手机号格式
     *
     * @param mobile 手机号
     * @return true表示格式正确，false表示格式错误
     */
    private boolean isValidMobile(String mobile) {
        if (mobile == null || mobile.trim().isEmpty()) {
            return false;
        }
        // 简单的手机号格式验证（中国大陆）
        return mobile.matches("^1[3-9]\\d{9}$");
    }
    
    /**
     * 短信验证码登录
     * 使用手机号和短信验证码进行登录
     *
     * @param smsLoginDTO 短信登录请求参数
     * @param request HTTP请求对象，用于获取客户端信息
     * @return 短信登录响应信息，包含用户信息和令牌
     * @throws BusinessException 当登录失败时抛出业务异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public SmsLoginVO smsLogin(SmsLoginDTO smsLoginDTO, HttpServletRequest request) {
        try {
            log.info("短信验证码登录请求: phone={}", smsLoginDTO.getPhone());

            // 1. 参数校验
            validateSmsLoginParams(smsLoginDTO);

            // 2. 验证短信验证码
            boolean isValidCode = smsService.verifySmsCode(smsLoginDTO.getPhone(), smsLoginDTO.getSmsCode(), "LOGIN");
            if (!isValidCode) {
                log.warn("短信验证码验证失败: phone={}", smsLoginDTO.getPhone());
                throw new BusinessException(ResultCode.SMS_CODE_INVALID);
            }

            // 3. 查询或创建用户
            User user = getUserByPhone(smsLoginDTO.getPhone());
            String clientIp = getClientIp(request);
            
            if (user == null) {
                // 新用户，创建账户
                user = createUserByPhone(smsLoginDTO.getPhone(), clientIp);
                log.info("创建新用户: phone={}, userId={}", smsLoginDTO.getPhone(), user.getId());
            } else {
                // 检查用户状态
                checkUserStatus(user);
                // 重置登录失败次数
                resetLoginFailCount(user.getId());
            }

            // 4. 更新最后登录信息
            updateLastLoginInfo(user.getId(), clientIp);

            // 5. 生成令牌
            StpUtil.login(user.getId());
            String accessToken = StpUtil.getTokenValue();
            String refreshToken = StpUtil.getTokenValue() + "_refresh"; // 简化的刷新令牌

            // 6. 构建登录响应
            UserVO userVO = UserVO.builder()
                    .id(user.getId())
                    .username(user.getUsername())
                    .phone(user.getPhone())
                    .email(user.getEmail())
                    .realName(user.getRealName())
                    .avatar(user.getAvatar())
                    .role(user.getRole())
                    .roleName(getRoleName(user.getRole()))
                    .status(user.getStatus())
                    .statusName(getStatusName(user.getStatus()))
                    .createTime(user.getCreateTime())
                    .lastLoginTime(user.getLastLoginTime())
                    .build();

            SmsLoginVO smsLoginVO = new SmsLoginVO();
            smsLoginVO.setExpiresIn(StpUtil.getTokenTimeout());
            smsLoginVO.setUserInfo(userVO);
            smsLoginVO.setNeedSetPassword(user.getPasswordSet() == null || !user.getPasswordSet());

            log.info("短信验证码登录成功: userId={}, phone={}, needSetPassword={}", 
                    user.getId(), user.getPhone(), smsLoginVO.getNeedSetPassword());
            return smsLoginVO;

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("短信验证码登录异常: phone={}", smsLoginDTO.getPhone(), e);
            throw new BusinessException(ResultCode.INTERNAL_SERVER_ERROR, "登录失败");
        }
    }

    /**
     * 根据手机号查询用户
     * 
     * @param phone 手机号码
     * @return 用户实体对象，如果不存在则返回null
     */
    @Override
    public User getUserByPhone(String phone) {
        if (StrUtil.isBlank(phone)) {
            return null;
        }

        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getPhone, phone);
        queryWrapper.eq(User::getDeleted, 0);

        return this.getOne(queryWrapper);
    }

    /**
     * 创建新用户（通过手机号）
     * 
     * @param phone 手机号码
     * @param clientIp 客户端IP地址
     * @return 新创建的用户实体对象
     * @throws BusinessException 当创建失败时抛出业务异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public User createUserByPhone(String phone, String clientIp) {
        try {
            log.info("创建新用户: phone={}, clientIp={}", phone, clientIp);

            // 检查手机号是否已存在
            if (checkPhoneExists(phone)) {
                throw new BusinessException(ResultCode.PHONE_ALREADY_EXISTS);
            }

            // 创建用户对象
            User user = new User();
            user.setPhone(phone);
            // 默认用户名：BugSeeker + UUID前6位
            user.setUsername("BugSeeker" + java.util.UUID.randomUUID().toString().substring(0, 6));
            user.setRole(3); // 默认角色：测试工程师
            user.setStatus(1); // 默认状态：正常
            user.setPasswordSet(false); // 未设置密码
            user.setLoginFailCount(0);
            user.setLastLoginIp(clientIp);
            user.setLastLoginTime(LocalDateTime.now());
            user.setCreateTime(LocalDateTime.now());
            user.setUpdateTime(LocalDateTime.now());
            user.setDeleted(0);

            // 保存用户
            boolean saved = this.save(user);
            if (!saved) {
                throw new BusinessException(ResultCode.DATABASE_ERROR, "创建用户失败");
            }

            log.info("新用户创建成功: userId={}, phone={}", user.getId(), phone);
            return user;

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("创建用户异常: phone={}", phone, e);
            throw new BusinessException(ResultCode.SYSTEM_ERROR, "创建用户失败");
        }
    }

    /**
     * 设置用户密码
     * 用于首次登录后设置密码
     *
     * @param userId 用户ID
     * @param setPasswordDTO 设置密码请求参数
     * @return true表示设置成功，false表示设置失败
     * @throws BusinessException 当设置失败时抛出业务异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean setPassword(Long userId, SetPasswordDTO setPasswordDTO) {
        try {
            log.info("设置用户密码: userId={}", userId);

            // 1. 参数校验
            validateSetPasswordParams(setPasswordDTO);

            // 2. 查询用户
            User user = this.getById(userId);
            if (user == null) {
                throw new BusinessException(ResultCode.USER_NOT_FOUND);
            }

            // 3. 检查两次密码是否一致
            if (!setPasswordDTO.getPassword().equals(setPasswordDTO.getConfirmPassword())) {
                throw new BusinessException(ResultCode.PASSWORD_MISMATCH);
            }

            // 4. 加密密码
            String encodedPassword = BCrypt.hashpw(setPasswordDTO.getPassword());

            // 5. 更新用户密码
            LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(User::getId, userId);
            updateWrapper.set(User::getPassword, encodedPassword);
            updateWrapper.set(User::getPasswordSet, true);
            updateWrapper.set(User::getUpdateTime, LocalDateTime.now());

            boolean updated = this.update(updateWrapper);
            if (!updated) {
                throw new BusinessException(ResultCode.DATABASE_ERROR, "设置密码失败");
            }

            log.info("用户密码设置成功: userId={}", userId);
            return true;

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("设置用户密码异常: userId={}", userId, e);
            throw new BusinessException(ResultCode.SYSTEM_ERROR, "设置密码失败");
        }
    }

    /**
     * 检查手机号是否已存在
     *
     * @param phone 手机号码
     * @return true表示已存在，false表示不存在
     */
    @Override
    public boolean checkPhoneExists(String phone) {
        if (StrUtil.isBlank(phone)) {
            return false;
        }

        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getPhone, phone);
        queryWrapper.eq(User::getDeleted, 0);

        return this.count(queryWrapper) > 0;
    }

    /**
     * 校验短信登录参数
     *
     * @param smsLoginDTO 短信登录请求参数
     * @throws BusinessException 当参数校验失败时抛出业务异常
     */
    private void validateSmsLoginParams(SmsLoginDTO smsLoginDTO) {
        // 手机号校验
        if (smsLoginDTO.getPhone() == null || smsLoginDTO.getPhone().trim().isEmpty()) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "手机号不能为空");
        }

        // 手机号格式校验
        if (!isValidMobile(smsLoginDTO.getPhone())) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "手机号格式不正确");
        }

        // 验证码校验
        if (smsLoginDTO.getSmsCode() == null || smsLoginDTO.getSmsCode().trim().isEmpty()) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "验证码不能为空");
        }

        // 验证码格式校验
        if (!smsLoginDTO.getSmsCode().matches("^\\d{6}$")) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "验证码格式不正确");
        }
    }

    /**
     * 校验设置密码参数
     *
     * @param setPasswordDTO 设置密码请求参数
     * @throws BusinessException 当参数校验失败时抛出业务异常
     */
    private void validateSetPasswordParams(SetPasswordDTO setPasswordDTO) {
        // 密码校验
        if (setPasswordDTO.getPassword() == null || setPasswordDTO.getPassword().trim().isEmpty()) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "密码不能为空");
        }

        // 确认密码校验
        if (setPasswordDTO.getConfirmPassword() == null || setPasswordDTO.getConfirmPassword().trim().isEmpty()) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "确认密码不能为空");
        }

        // 密码长度校验
        if (setPasswordDTO.getPassword().length() < 6 || setPasswordDTO.getPassword().length() > 20) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "密码长度应在6-20位之间");
        }
    }
}